// Copyright (c) 2009, Tom Lokovic
// All rights reserved.
// 
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are met:
//
//     * Redistributions of source code must retain the above copyright notice,
//       this list of conditions and the following disclaimer.
//     * Redistributions in binary form must reproduce the above copyright
//       notice, this list of conditions and the following disclaimer in the
//       documentation and/or other materials provided with the distribution.
// 
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
// POSSIBILITY OF SUCH DAMAGE.

using System;

namespace Byond.Midi
{
	/// <summary>
	/// Description of a scale's pattern as it ascends through an octave.
	/// </summary>
	/// <remarks>
	/// This class describes the general behavior of a scale as it ascends from a tonic up to
	/// the next tonic.  It is described in terms of semitones relative to the tonic; to apply it to
	/// a particular tonic, pass one of these to the constructor of <see cref="Scale"/>.
	/// </remarks>
	public class ScalePattern
	{
		/// <summary>
		/// Constructs a scale pattern.
		/// </summary>
		/// <param name="name">The name of the scale pattern.</param>
		/// <param name="ascent">The ascending pattern of the scale.  See the <see cref="Ascent"/>
		/// property for a detailed description and requirements.  This parameter is copied.</param>
		/// <exception cref="ArgumentException">name or ascent is null.</exception>
		/// <exception cref="ArgumentNullException">ascent is invalid.</exception>
		public ScalePattern(string name, int[] ascent)
		{
			if (name == null || ascent == null)
				throw new ArgumentNullException();
			// Make sure ascent is valid.
			if (!AscentIsValid(ascent))
				throw new ArgumentException("ascent is invalid.");
			Name = string.Copy(name);
			Ascent = new int[ascent.Length];
			Array.Copy(ascent, Ascent, ascent.Length);
		}

		/// <summary>The name of the scale being described.</summary>
		public string Name { get; private set; }

		/// <summary>The ascent of the scale.</summary>
		/// <remarks>
		/// <para>The ascent is expressed as a series of integers, each giving a semitone
		/// distance above the tonic. It must have at least two elements, start at zero (the
		/// tonic), be monotonically increasing, and stay below 12 (the next tonic above).</para>
		/// <para>The number of elements in the ascent tells us how many notes-per-octave in the
		/// scale.  For example, a heptatonic scale will always have seven elements in the ascent.
		/// </para>
		/// </remarks>
		public int[] Ascent { get; private set; }

		/// <summary>Returns true if ascent is valid.</summary>
		private bool AscentIsValid(int[] ascent)
		{
			// Make sure it is non-empty, starts at zero, and ends before 12.
			if (ascent.Length < 2 || ascent[0] != 0 || ascent[ascent.Length - 1] >= 12)
				return false;
			// Make sure it's monotonically increasing.
			for (var i = 1; i < ascent.Length; ++i)
			{
				if (ascent[i] <= ascent[i - 1])
					return false;
			}
			return true;
		}

		#region Operators, Equality, Hash Codes

		/// <summary>
		/// ToString returns the pattern name.
		/// </summary>
		/// <returns>The pattern's name, such as "Major" or "Melodic Minor (ascending)".</returns>
		public override string ToString()
		{
			return Name;
		}

		/// <summary>
		/// Equality operator does value equality.
		/// </summary>
		public static bool operator ==(ScalePattern a, ScalePattern b)
		{
			return ReferenceEquals(a, b) || a.Equals(b);
		}

		/// <summary>
		/// Inequality operator does value inequality.
		/// </summary>
		public static bool operator !=(ScalePattern a, ScalePattern b)
		{
			return !(a == b);
		}

		/// <summary>
		/// Value equality.
		/// </summary>
		public override bool Equals(Object obj)
		{
			var other = obj as ScalePattern;
			if ((Object) other == null)
				return false;
			if (!Name.Equals(other.Name))
				return false;
			if (Ascent.Length != other.Ascent.Length)
				return false;
			for (var i = 0; i < Ascent.Length; ++i)
			{
				if (Ascent[i] != other.Ascent[i])
					return false;
			}
			return true;
		}

		/// <summary>
		/// Hash code.
		/// </summary>
		public override int GetHashCode()
		{
			// TODO
			return 0;
		}

		#endregion
	}
}